<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>复习js类</title>
</head>
<body>
    <script type="text/javascript">
        // 定义一个类
        class Persion {
            // 定义构造器（若不需要也可不定义）
            constructor(name, age) {
                this.name = name    // 类对象所有，不属于类本身
                this.age = age      // 类对象所有，不属于类本身
            }

            // 定义类方法，放在类的原型链上，属于类本身，不属于任何创建的对象
            speak() {
                // ``为es6的模版
                // 此处的this指向调用类的对象本身
                console.log(`1 Persion 我叫${this.name}，今年${this.age}岁了！！`)
            }

            speak2() {
                console.log(`2 Persion 我叫${this.name}，今年${this.age}岁了！！`)
            }

            speak3() {
                console.log(`3 Persion 我叫${this.name}，今年${this.age}岁了！！`)
            }

            // 类对象所有，不属于类本身
            ancestor = 'Adam and Eve'

            // 普通函数的this指向调用着，另外，类方法局部隐式开启了严格模式，'use strict'
            crawl = function () {
                // 'use strict' // 隐式开启
                console.log("crawl:", this)
            }

            // 箭头函数的this上一级this
            walk = () => {
                console.log("walk:", this)
            }

            // walk = () => console.log("walk:", this)  // 单语句可省略 {}
            // walk = a => a+1  // 一个入参可省略()
        }

        // 创建类对象
        const p1 = new Persion()
        const p2 = new Persion('lxd', 20)
        const p3 = new Persion('fan', 18)

        // 查看对象
        console.log(p1)
        console.log(p2)
        console.log(p3)

        // 调用类方法
        p1.speak()
        p2.speak()
        p3.speak()
        p1.speak.call(new Persion('obj', 100))  // call, bind, apply 将改变调用对象

        const crawl = p2.crawl
        p2.crawl()  // this 指向 p2
        crawl()     // this undefined

        const walk = p2.walk
        p2.walk()   // this 指向 p2
        walk()      // this 指向 p2

        // ================ 继承 ====================
        class Student extends Persion {
            // 重写自己的构造器，也可不写
            constructor(name, age, grade) {
                // 必须调用super()方法调用父类构造器，且必须放在最前面
                super(name, age)
                // 添加自己的实现
                this.grade = grade
            }

            // 重写父类方法，将覆盖父类方法
            speak2() {
                console.log(`2 Student 我叫${this.name}，今年${this.age}岁，${this.grade}年级。`)
            }

            // 虽然加了一个参数，但也是重写父类方法，当未传参数时，gender=undefined
            speak3(gender) {
                console.log(`3 Student 我叫${this.name}，性别${gender}，今年${this.age}岁，${this.grade}年级。`)
            }
        }

        const s1 = new Student('lxd', 18, '大一')
        console.log(s1)

        s1.speak()      // 调用父类的speak方法，
        s1.speak2()     // 调用自己的speak2方法，
        s1.speak3()     // 调用自己的speak3方法，并没有调用父类Person的speak3方法，gender=undefined
        s1.speak3('男') // 调用自己的speak3方法，

        // ============= 类属性 =================
        const type = 'phone'
        const opacity = 1
        // 创建对象
        const Info1 = {
            'username': 'demo', // 属性名为字符串
            password: '12345',  // 语法糖，属性名省略''
            [type]: '18138805937',  // 属性名为变量
            opacity,     // 当对象的key和要设置的value的变量同名，可省略value，此处等价于 opacity: 1,
        }

        console.log('Info1:', Info1)

        // 给对象添加属性
        const Info2 = {}
        Info2['email'] = '916643534@qq.com'  // 字符串 ==> 属性名为'email'
        Info2.age = 18                       // .的方式 ==> 属性名为'age'
        const key = 'address'
        Info2[key] = '深圳'                  // 变量 ==> 属性名为'address'

        console.log('Info2:', Info2)

    </script>
</body>
</html>